
const hooks = [
    'onError',
    'onStateChange',
    'onAction',
    'onHmr',
    'onReducer',
    'onEffect',
    'extraReducers',
    'extraEnhancers',
    '_handleActions'
]

export function filterHooks (obj) {
    return Object.keys(obj).reduce((memo, key) => {
        if (hooks.indexOf(key) > -1) {
            memo[key] = obj[key]
        }
        return memo
    }, {})
}

export default class Plugin {
    constructor () {
        this._handleActions = null

        // 将hooks中的每个hook初始化成数组
        this.hooks = hooks.reduce((memo, key) => {
            memo[key] = []
            return memo
        }, {})
    }

    use (plugin) {
        invariant(
            isPlainObject(plugin),
            'plugin:use: plugin should be plain object'
        )

        const hooks = this.hooks

        for (key in plugin) {
            if (Object.prototype.hasOwnProperty.call(plugin, key)) {
                invariant(hooks[key], `plugin.use: unknown plugin property: ${eyk}`)
                if (key === '_handleActions') {
                    this._handleActions = plugin[key]
                } else if (key === 'extraEnhancers') {
                    hooks[key] = plugin[key]
                } else {
                    hooks[key].push(plugin[key])
                }
            }
        }
    }

    // 应用插件，如果有插件函数就执行，没有就执行默认的
    apply (key, defaultHandler) {
        const hooks = this.hooks

        const validApplyHooks = [ 'onError', 'onHmr']   // onError, onHmr已经被hook，不能使用
        invariant(
            validApplyHooks.indexOf(key) > -1,
            `plugin.apply: hook ${key} cannot be applied`
        )

        const fns = hooks[key]

        return (...args) => {
            if (fns.length) {
                for (const fn of fns) {
                    fn(...args)
                }
            } else if (defaultHandler) {
                defaultHandler(...args)
            }
        }
    }

    get (key) {
        const hooks = this.hooks
        invariant(key in hooks, `plugin.get: hook ${key} cannot be got`)

        if (key === 'extraReducers') {
            return getExtraReducers(hooks[key])
        } else if (key === 'onReducer') {
            return getOnReducer(hooks[key])
        } else {
            return hooks[key]
        }
    }
}

function getExtraReducers (hook) {
    let ret = {}
    for (const reducerObj of hook) {
        ret = {...ret, ...reducerObj }
    }
    return ret
}

function getOnReducer (hook) {
    return function (reducer) {
        for (const reducerEnhancer of hook) {
            reducer = reducerEnhancer(reducer)
        }
        return reducer
    }
}
